package fr.smile.liferay.portlet.news.service.persistence;

import java.sql.Timestamp;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.liferay.portal.kernel.dao.orm.ORMException;
import com.liferay.portal.kernel.dao.orm.QueryPos;
import com.liferay.portal.kernel.dao.orm.QueryUtil;
import com.liferay.portal.kernel.dao.orm.SQLQuery;
import com.liferay.portal.kernel.dao.orm.Session;
import com.liferay.portal.kernel.dao.orm.Type;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.util.CalendarUtil;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.service.persistence.impl.BasePersistenceImpl;
import com.liferay.util.dao.orm.CustomSQLUtil;

import fr.smile.liferay.portlet.news.model.NewsEntry;
import fr.smile.liferay.portlet.news.model.impl.NewsEntryImpl;

public class NewsEntryFinderImpl extends BasePersistenceImpl<NewsEntry> implements NewsEntryFinder {
	public static final String COUNT_UNREAD = NewsEntryFinder.class.getName() + ".countUnread";
	public static final String FIND_UNREAD = NewsEntryFinder.class.getName() + ".findUnread";
	public static final String FIND_PUBLISHED_BETWEEN = NewsEntryFinder.class.getName() + ".findPublishedBetween";

	@SuppressWarnings("unchecked")
	public List<NewsEntry> findPublishedBetween(Date from, Date to) throws SystemException {
		Timestamp from_TS = CalendarUtil.getTimestamp(from);
		Timestamp to_TS = CalendarUtil.getTimestamp(to);

		Session session = null;
		try {
			session = openSession();

			String sql = CustomSQLUtil.get(FIND_PUBLISHED_BETWEEN);

			SQLQuery q = session.createSQLQuery(sql);

			q.addEntity("News_NewsEntry", NewsEntryImpl.class);

			QueryPos qPos = QueryPos.getInstance(q);

			qPos.add(to_TS);
			qPos.add(from_TS);

			return q.list();
		} catch (ORMException e) {
			throw new SystemException(e);
		} finally {
			closeSession(session);
		}
	}

	@SuppressWarnings("unchecked")
	public List<NewsEntry> findUnread(long userId, Map<Long, long[]> scopes, int start, int end) throws SystemException {
		Session session = null;

		try {
			session = openSession();

			String sql = CustomSQLUtil.get(FIND_UNREAD);

			sql = StringUtil.replace(sql, "[$CLASS_PKS$]", getClassPKs(scopes));
			sql = CustomSQLUtil.replaceAndOperator(sql, true);

			SQLQuery q = session.createSQLQuery(sql);

			q.addEntity("News_NewsEntry", NewsEntryImpl.class);

			QueryPos qPos = QueryPos.getInstance(q);

			setClassPKs(qPos, scopes);

			Date now = new Date();
			setDates(qPos, now, now);

			qPos.add(userId);

			return (List<NewsEntry>) QueryUtil.list(q, getDialect(), start, end);
		} catch (Exception e) {
			throw new SystemException(e);
		} finally {
			closeSession(session);
		}
	}

	public int countUnread(long userId, Map<Long, long[]> scopes) throws SystemException {
		Session session = null;

		try {
			session = openSession();

			String sql = CustomSQLUtil.get(COUNT_UNREAD);

			sql = StringUtil.replace(sql, "[$CLASS_PKS$]", getClassPKs(scopes));
			sql = CustomSQLUtil.replaceAndOperator(sql, true);

			SQLQuery q = session.createSQLQuery(sql);

			q.addScalar(COUNT_COLUMN_NAME, Type.LONG);

			QueryPos qPos = QueryPos.getInstance(q);

			setClassPKs(qPos, scopes);

			Date now = new Date();
			setDates(qPos, now, now);

			qPos.add(userId);

			Number count = (Number) q.uniqueResult();
			return count.intValue();
		} catch (ORMException e) {
			throw new SystemException(e);
		} finally {
			closeSession(session);
		}
	}

	protected String getClassPKs(long classNameId, long[] classPKs) {
		if (classPKs.length == 0) {
			return "(News_NewsEntry.classNameId = ?) AND (";
		}

		StringBundler sb = new StringBundler(classPKs.length * 2 + 1);

		sb.append("(News_NewsEntry.classNameId = ?) AND (");

		for (int i = 0; i < classPKs.length; i++) {
			sb.append("(News_NewsEntry.classPK = ?)");

			if ((i + 1) < classPKs.length) {
				sb.append(" OR ");
			} else {
				sb.append(")");
			}
		}

		return sb.toString();
	}

	protected String getClassPKs(Map<Long, long[]> scopes) {
		if ((scopes == null) || scopes.isEmpty()) {
			return StringPool.BLANK;
		}

		StringBundler sb = new StringBundler(scopes.size() * 4 - 1);

		Iterator<Map.Entry<Long, long[]>> itr = scopes.entrySet().iterator();
		while (itr.hasNext()) {
			Map.Entry<Long, long[]> entry = itr.next();

			Long classNameId = entry.getKey();
			long[] classPKs = entry.getValue();

			sb.append("(");
			sb.append(getClassPKs(classNameId.longValue(), classPKs));
			sb.append(")");

			if (itr.hasNext()) {
				sb.append(" OR ");
			}
		}

		return sb.toString();
	}

	protected void setClassPKs(QueryPos qPos, long classNameId, long[] classPKs) {
		qPos.add(classNameId);

		for (long classPK : classPKs) {
			qPos.add(classPK);
		}
	}

	protected void setClassPKs(QueryPos qPos, Map<Long, long[]> scopes) {
		if (scopes == null) {
			return;
		}

		for (Map.Entry<Long, long[]> entry : scopes.entrySet()) {
			long classNameId = entry.getKey().longValue();
			long[] classPKs = entry.getValue();

			setClassPKs(qPos, classNameId, classPKs);
		}
	}

	protected void setDates(QueryPos qPos, Date displayDate, Date expirationDate) {
		Timestamp displayDateTS = CalendarUtil.getTimestamp(displayDate);
		Timestamp expirationDateTS = CalendarUtil.getTimestamp(expirationDate);

		qPos.add(displayDateTS);
		qPos.add(displayDateTS);
		qPos.add(expirationDateTS);
		qPos.add(expirationDateTS);
	}
}
